/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 北京银杉金服科技有限公司
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2018/9/9 15:11
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.util;

import com.hyacinth.entity.User;
import com.hyacinth.vo.UserVo;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;


/**
 * 对象拷贝通用工具类
 *
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
public class BeanUtils {

    private final Map<String, PropertyDescriptor> propertyDescriptorCache = new LinkedHashMap<>();

    /**
     * 拷贝PO对象到VO对象中，所有对应的字段都会进行拷贝
     * @param source 原对象
     * @param target 目标对象
     */
    public static void copyAllProperties(Object source, Object target) throws Exception {
        copyAllProperties(source, target, null);
    }

    /**
     * 拷贝PO对象到VO对象中，所有对应的字段都会进行拷贝
     * @param source 原对象
     * @param target 目标对象
     * @param ignoreProperties 忽略属性
     */
    public static void copyAllProperties(Object source,
                                         Object target,
                                         String[] ignoreProperties) throws Exception {
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
        for (PropertyDescriptor targetPd : targetPds) {
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            Object value = readMethod.invoke(source);
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            writeMethod.invoke(target, value);
                        } catch (Throwable ex) {
                            throw new RuntimeException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }
                }
            }
        }
    }

    private static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws Exception {
        PropertyUtils pu = PropertyUtils.forClass(clazz);
        return pu.getPropertyDescriptors();
    }

    private static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName) throws Exception {
        PropertyUtils pu = PropertyUtils.forClass(clazz);
        return pu.getPropertyDescriptor(propertyName);
    }

    public static void main(String[] args) throws Exception {
        User user = new User();
        user.setId(1L);
        user.setCreateTime(new Date());
        user.setDataFlag("0");

        user.setEmail("12222@qq.com");
        user.setName("zhangfb");
        user.setPassWord("11111");
        UserVo vo = new UserVo();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            copyAllProperties(user,vo);
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时ms="+(end-start));
        System.out.println(vo);
    }
}
